1
2
3
4
5
6
7 package ca.uhn.cache.impl;
8
9 import java.util.ArrayList;
10 import java.util.Arrays;
11 import java.util.Iterator;
12 import java.util.List;
13
14 import org.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16 import org.springframework.beans.factory.InitializingBean;
17
18 import ca.uhn.cache.IDimension;
19 import ca.uhn.cache.IParamSpace;
20 import ca.uhn.cache.IQuery;
21 import ca.uhn.cache.IQueryParam;
22 import ca.uhn.cache.internal.IParamSpaceConfig;
23
24 /***
25 * A multi-dimensional space of <code>IQueryParam</code>s. A data item
26 * has a location in this space (see <code>IDataInspector</code>). Data
27 * items are divided into <code>IChunk</code>s that represent regions of the
28 * space.
29 *
30 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
31 * @version $Revision: 1.1 $ updated on $Date: 2005/01/24 22:52:39 $ by $Author: bryan_tripp $
32 */
33 public class ParamSpace implements IParamSpace, InitializingBean {
34
35 private static final Log ourLog = LogFactory.getLog( ParamSpace.class );
36
37 private IParamSpaceConfig myParamSpaceConfig;
38
39 /***
40 * Factory method.
41 *
42 * @param theParamSpaceConfig The configuration information for this space (defining dimensions, etc).
43 * @return The created <code>ParamSpace</code>.
44 */
45 public static ParamSpace createInstance( IParamSpaceConfig theParamSpaceConfig ) {
46 ParamSpace retVal = new ParamSpace();
47
48 retVal.setParamSpaceConfig(theParamSpaceConfig);
49
50 return retVal;
51 }
52
53 /***
54 */
55 public ParamSpace() {
56 }
57
58 /***
59 * {@inheritDoc}
60 */
61 public IDimension[] getDimensions()
62 {
63 return myParamSpaceConfig.getDimensions();
64 }
65
66 /***
67 * {@inheritDoc}
68 */
69 public IQueryParam[] chunk(IQueryParam theParam)
70 {
71 IQueryParam[] result = null;
72
73 if (myParamSpaceConfig.isChunked(theParam.getDimension())) {
74 IQueryParam[] all = myParamSpaceConfig.getChunkBoundaries(theParam.getDimension());
75
76 List intersecting = new ArrayList();
77 for (int i = 0; i < all.length; i++) {
78 if (theParam.intersects(all[i])) {
79 intersecting.add(all[i]);
80 }
81 }
82
83
84
85 if (intersecting.size() == 0) {
86 ourLog.warn("An IQueryParam intersects 0 chunks. ParamSpace may not be configured properly");
87 }
88
89 result = (IQueryParam[]) intersecting.toArray(new IQueryParam[ intersecting.size() ]);
90 } else {
91
92
93
94 result = new IQueryParam[] {theParam};
95 }
96
97 return result;
98 }
99
100 /***
101 * {@inheritDoc}
102 */
103 public IQuery[] group( IQuery[] theQueries, int theMaxGroups ) {
104 IQuery[] retVal = theQueries;
105
106 if ( theMaxGroups > 0 ) {
107 do {
108 retVal = doGroup(retVal, theMaxGroups);
109 } while (retVal.length > theMaxGroups);
110 }
111
112 return retVal;
113 }
114
115
116 private IQuery[] doGroup(IQuery[] theQueries, int theMaxGroups) {
117 List grouped = new ArrayList(theQueries.length);
118 List ungrouped = new ArrayList(Arrays.asList(theQueries));
119
120 while (ungrouped.size() > 0 && (grouped.size() + ungrouped.size()) > theMaxGroups) {
121 IQuery toGroup = (IQuery) ungrouped.remove(0);
122 IQuery closestGrouped = findClosest(toGroup, grouped);
123 IQuery closestUngrouped = findClosest(toGroup, ungrouped);
124
125 if (isCloser(toGroup, closestUngrouped, closestGrouped)) {
126 ungrouped.remove(closestUngrouped);
127 grouped.add(merge(toGroup, closestUngrouped));
128 } else {
129 grouped.remove(closestGrouped);
130 grouped.add(merge(toGroup, closestGrouped));
131 }
132 }
133
134 grouped.addAll(ungrouped);
135 return (IQuery[]) grouped.toArray(new IQuery[0]);
136 }
137
138
139
140 private boolean isCloser(IQuery theFrom, IQuery theTo, IQuery theOtherTo) {
141 if (theTo == null) {
142 return false;
143 } else if (theOtherTo == null) {
144 return true;
145 } else {
146 return (distance(theFrom, theTo) < distance(theFrom, theOtherTo)) ? true : false;
147 }
148 }
149
150
151 private IQuery merge(IQuery theQuery, IQuery theOther) {
152 Query result = new Query();
153
154 if (theQuery.getParameters().size() != theOther.getParameters().size()) {
155 throw new IllegalArgumentException("Can't merge queries with different "
156 + "numbers of dimensions");
157 }
158
159 Iterator params = theQuery.getParameters().iterator();
160 while (params.hasNext()) {
161 IQueryParam oneParam = (IQueryParam) params.next();
162 IQueryParam otherParam = getParam(theOther, oneParam.getDimension());
163 result.addParameter(oneParam.merge(otherParam));
164 }
165
166 return result;
167 }
168
169
170
171
172 private IQuery findClosest(IQuery theQuery, List theQueries) {
173 float shortestDistance = 10000f;
174 IQuery closest = null;
175
176 for (int i = 0; i < theQueries.size(); i++) {
177 float distance = distance(theQuery, (IQuery) theQueries.get(i));
178 if (distance < shortestDistance) {
179 shortestDistance = distance;
180 closest = (IQuery) theQueries.get(i);
181 }
182 }
183
184 return closest;
185 }
186
187
188 private float distance(IQuery theQuery, IQuery theOtherQuery) {
189 float squaredResult = 0;
190
191 Iterator params = theQuery.getParameters().iterator();
192 while (params.hasNext()) {
193 IQueryParam oneParam = (IQueryParam) params.next();
194 IQueryParam otherParam = getParam(theOtherQuery, oneParam.getDimension());
195 float distance = oneParam.getDistance(otherParam,
196 myParamSpaceConfig.getSaturationPoint(oneParam.getDimension()));
197 squaredResult += (distance * distance);
198 }
199
200 return (float) Math.sqrt(squaredResult);
201 }
202
203
204 private IQueryParam getParam(IQuery theQuery, IDimension theDimension) {
205 IQueryParam result = null;
206
207 Iterator params = theQuery.getParameters().iterator();
208 while (params.hasNext() && result == null) {
209 IQueryParam param = (IQueryParam) params.next();
210 if (param.getDimension().equals(theDimension)) {
211 result = param;
212 }
213 }
214
215 if (result == null) {
216 throw new IllegalArgumentException("Query has no parameter of dimension "
217 + theDimension.getName());
218 }
219
220 return result;
221 }
222
223
224
225 /***
226 * @return The configuration information for this space (defining dimensions, etc).
227 */
228 public IParamSpaceConfig getParamSpaceConfig() {
229 return myParamSpaceConfig;
230 }
231 /***
232 * @param theConfigData The configData to set.
233 */
234 public void setParamSpaceConfig( IParamSpaceConfig theConfigData ) {
235 myParamSpaceConfig = theConfigData;
236 }
237
238 /***
239 * {@inheritDoc}
240 */
241 public void afterPropertiesSet() throws Exception {
242 assert getParamSpaceConfig() != null : "getParamSpaceConfig() != null";
243 }
244
245
246
247
248 }